EDA. Red Wine Quality

Выполнила Вощинина Мария

In [76]:
%matplotlib inline

import os
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pandas_profiling
import seaborn as sns
import plotly.express as px
from sklearn.preprocessing import MinMaxScaler
from sklearn import preprocessing

Загрузка данных

In [2]:
df = pd.read_csv('winequality-red.csv')
In [130]:
df.shape
Out[130]:
(1599, 12)
In [3]:
df.head(7)
Out[3]:
fixed acidity volatile acidity citric acid residual sugar chlorides free sulfur dioxide total sulfur dioxide density pH sulphates alcohol quality
0 7.4 0.70 0.00 1.9 0.076 11.0 34.0 0.9978 3.51 0.56 9.4 5
1 7.8 0.88 0.00 2.6 0.098 25.0 67.0 0.9968 3.20 0.68 9.8 5
2 7.8 0.76 0.04 2.3 0.092 15.0 54.0 0.9970 3.26 0.65 9.8 5
3 11.2 0.28 0.56 1.9 0.075 17.0 60.0 0.9980 3.16 0.58 9.8 6
4 7.4 0.70 0.00 1.9 0.076 11.0 34.0 0.9978 3.51 0.56 9.4 5
5 7.4 0.66 0.00 1.8 0.075 13.0 40.0 0.9978 3.51 0.56 9.4 5
6 7.9 0.60 0.06 1.6 0.069 15.0 59.0 0.9964 3.30 0.46 9.4 5
In [4]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1599 entries, 0 to 1598
Data columns (total 12 columns):
fixed acidity           1599 non-null float64
volatile acidity        1599 non-null float64
citric acid             1599 non-null float64
residual sugar          1599 non-null float64
chlorides               1599 non-null float64
free sulfur dioxide     1599 non-null float64
total sulfur dioxide    1599 non-null float64
density                 1599 non-null float64
pH                      1599 non-null float64
sulphates               1599 non-null float64
alcohol                 1599 non-null float64
quality                 1599 non-null int64
dtypes: float64(11), int64(1)
memory usage: 150.0 KB
In [5]:
df.columns
Out[5]:
Index(['fixed acidity', 'volatile acidity', 'citric acid', 'residual sugar',
       'chlorides', 'free sulfur dioxide', 'total sulfur dioxide', 'density',
       'pH', 'sulphates', 'alcohol', 'quality'],
      dtype='object')
In [6]:
features = ['fixed acidity', 'volatile acidity', 'citric acid', 'residual sugar',
           'chlorides', 'free sulfur dioxide', 'total sulfur dioxide', 'density',
           'pH', 'sulphates', 'alcohol']
target = ['quality']

Инсайды с Pandas-Profiling

По совету с лекции впервые использовала библиотку. Интресено, но не все понравилось (например, корреляция между переменными шлчит и не информативна как-то)

  • есть дубликаты(15%)
  • нет попущенных значений, возможно их уже заполнили. Далее попробую проверить эту гипотезу
  • citric_acid содержит 8% нулей, возможно ими заполнили пропущенные значения!
  • есть подозрительные значения у total sulfur dioxide и free sulfur dioxide, sulphates
  • фактически оценки ставили от 3 до 8,на оценки 5 и 6 приходится 83% наблюдений
  • total sulfur dioxide и free sulfur dioxide сильно коррелируют, также fixed acidity, volatile acidity (что логично)
In [7]:
pandas_profiling.ProfileReport(df)








Out[7]:

Boxplot

Выводы:

  • по некоторым признакам можно увидеть, что оценка качества линейно зависит от значений признака. Например, volatile acidity, citric acid,
  • у некоторых признаков, например residual sugar, много выбросов. Можно поробовать обработать этот признак
  • еще можно заметить, что выбросы в основном по оценкам качества 5 и 6
In [128]:
for i in features:
    plt.figure(i, figsize=(7,7))
    sns.set_palette("Blues")
    ax = sns.boxplot(x="quality", y=i, data=df) 
    plt.title('Boxplot для %s' %i) 

Гистограммы с большим количеством бинов

Выводы:

  • была гипотеза, что пропуски заполняют каким то значением.Она не подтвердилась
  • распределения некоторых величин похожи на нормальное. Например, pH
  • Некоторые переменные стоит прологарифмировать
In [129]:
for i in features:
    plt.hist(df[i], 70, alpha=0.75, color ='k')
    plt.xlabel('%s' %i)
    plt.title('Гистограмма %s' %i)
    plt.grid(True)
    plt.show()

Построим логарифмы для некоторых переменных

In [ ]:
Выводы:
    * после логарифмирования распределение признака chlorides стало позоже на нормальное.
In [126]:
log = ['residual sugar', 'chlorides', 'free sulfur dioxide', 'total sulfur dioxide']
for i in log:
    plt.hist(np.log(df[i]), 70, alpha=0.75, color ='k')
    plt.xlabel('%s' %i)
    plt.title('Гистограмма log(%s)' %i)
    plt.grid(True)
    plt.show()

Гистограммы с разрезом по качеству

Выводы:

  • По сути, этим графиком проверяются те же гипотезы, что и boxplot. В данном случае много классов, поэтому график не очень удобно читать и тяжело увидеть какие-то закономерности.
In [103]:
for i in features:
    for j in list(set(df['quality'])):       
        subset = df[df['quality'] == j]
        plt.figure(i, figsize=(15,3))
        sns.set_palette("Blues")
        ax = sns.distplot(subset[i],  hist = False, kde = True,
                  kde_kws = {'linewidth': 3}, 
                  label = j)        
        plt.legend(prop={'size': 16}, title = 'quality')
        plt.title('Гистограмма для %s' %i)
        plt.xlabel('%s' %i)

Зависимость между качеством и признаками

Построим на 50% выборки

Выводы:

  • Опять же, boxplot удобнее. Выводы такие же
In [125]:
for i in features:
    #plt.figure(i, figsize=(7,7))
    sns.set_palette("Blues", 1)
    ax = sns.jointplot(y=i, x="quality", data=df.sample(frac=0.5))
    plt.title('Диаграмма рассеяния для %s' %i, loc='center')
    #plt.subplots_adjust(hspace = 3)
    plt.xlabel('%s' %i)

Диаграмма параллельных координат (?)

In [57]:
df_means.columns
Out[57]:
Index(['fixed acidity', 'volatile acidity', 'citric acid', 'residual sugar',
       'chlorides', 'free sulfur dioxide', 'total sulfur dioxide', 'density',
       'pH', 'sulphates', 'alcohol', 'quality'],
      dtype='object')

Выводы:

  • Можно заметить, что есть переменные, для которых качество 3 и качество 8 находятся в противоположных концах. Такие переменные:

    • volatile acidity
    • citric acid
    • chlorides
    • pH
    • sulphates
    • alcohol

      Полагаю, что они как раз хорошо себя покажут в классификации

In [41]:
fig = px.parallel_coordinates(df.sample(frac=0.5),color="quality", color_continuous_scale=px.colors.diverging.Tealrose)
fig.show()

Вышло не очень наглядно. Попробуем построить средние значения

In [53]:
df_means = pd.DataFrame(df.groupby('quality', as_index = False).agg('mean'))
df1 = df_means.pop('quality')
df_means['quality'] = df1
df_means.head()
Out[53]:
fixed acidity volatile acidity citric acid residual sugar chlorides free sulfur dioxide total sulfur dioxide density pH sulphates alcohol quality
0 8.360000 0.884500 0.171000 2.635000 0.122500 11.000000 24.900000 0.997464 3.398000 0.570000 9.955000 3
1 7.779245 0.693962 0.174151 2.694340 0.090679 12.264151 36.245283 0.996542 3.381509 0.596415 10.265094 4
2 8.167254 0.577041 0.243686 2.528855 0.092736 16.983847 56.513950 0.997104 3.304949 0.620969 9.899706 5
3 8.347179 0.497484 0.273824 2.477194 0.084956 15.711599 40.869906 0.996615 3.318072 0.675329 10.629519 6
4 8.872362 0.403920 0.375176 2.720603 0.076588 14.045226 35.020101 0.996104 3.290754 0.741256 11.465913 7
In [54]:
fig = px.parallel_coordinates(df_means,color="quality", color_continuous_scale=px.colors.diverging.Tealrose)
fig.show()

Еще хотела добавить на график sd, но не получилось

Лепестковая диаграмма

Строим на основе таблицы из пункта выше, плюс нормируем все признаки.

Выводы:

  • в целом, выводы такие же как и в пункте выше
  • на таком графике хорошо видно, увеличение каких показателей приводит к ухудшению/улучшению качества вина
In [87]:
scaler = MinMaxScaler(feature_range=(0,1)) 
df_means_scaled = pd.DataFrame(scaler.fit_transform(df_means[features]), columns = features)
df_means_scaled['quality'] = df_means['quality']
In [120]:
labels = features
stats=df_means_scaled.loc[5,labels].values
stats0=df_means_scaled.loc[0,labels].values

angles = np.linspace(0, 2*np.pi, len(labels), endpoint=False)
stats = n p.concatenate((stats,[stats[0]]))
stats0 = np.concatenate((stats0,[stats0[0]]))
angles = np.concatenate((angles,[angles[0]]))

# Plot stuff
fig = plt.figure(figsize=(7,7))
ax = fig.add_subplot(111, polar=True)
ax.plot(angles, stats, 'o-', linewidth=2, color = 'r')
ax.fill(angles, stats, alpha=0.2, color = 'r')
ax.plot(angles, stats0, 'o-', linewidth=2, color = 'g')
ax.fill(angles, stats0, alpha=0.2, color = 'g')
ax.set_thetagrids(angles * 180/np.pi, labels)
ax.set_title("Сравнение вин разного качества")
ax.legend(('quality = 8', 'quality = 3'), loc =1)
ax.grid(True)
plt.show()